/*
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package fr.unice.guitartools;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.drawable.StateListDrawable;
import android.media.AudioManager;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.AudioColumns;
import android.provider.MediaStore.MediaColumns;
import android.text.Editable;
import android.text.method.LinkMovementMethod;
import android.text.SpannableString;
import android.text.TextWatcher;
import android.text.util.Linkify;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsoluteLayout;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;

import fr.unice.guitartools.soundfile.CheapSoundFile;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.UUID;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

/**
 * The activity for the Ringdroid main editor window.  Keeps track of
 * the waveform display, current horizontal offset, marker handles,
 * start / end text boxes, and handles all of the buttons and controls.
 */
public class RingdroidEditActivity extends Activity
implements MarkerView.MarkerListener,
WaveformView.WaveformListener
{
	private static String LoopExtension = ".guitartools";
	private long mLoadingStartTime;
	private long mLoadingLastUpdateTime;
	private boolean mLoadingKeepGoing;
	private boolean mSelectAllSong;
	private ProgressDialog mProgressDialog;
	private CheapSoundFile mSoundFile;
	private File mFile;
	private String mFilename;
	private String mLoopFileName;
	private String mDstFilename;
	private String mArtist;
	private String mAlbum;
	private String mGenre;
	private String mTitle;
	private int mYear;
	private String mExtension;
	private String mRecordingFilename;
	private int mNewFileKind;
	private Uri mRecordingUri;
	private boolean mWasGetContentIntent;
	private WaveformView mWaveformView;
	private MarkerView mStartMarker;
	private MarkerView mEndMarker;
	private TextView mStartText;
	private TextView mEndText;
	private TextView mInfo;
	private ImageButton mSelectAllButton;
	private ImageButton mPlayButton;
	private ImageButton mRewindButton;
	private ImageButton mFfwdButton;
	private ImageButton mZoomInButton;
	private ImageButton mZoomOutButton;
	private ImageButton mSaveButton;
	private ImageButton mSaveButton_2;
	private ImageButton mDeleteLoop;
	private boolean mKeyDown;
	private String mCaption = "";
	private int mWidth;
	private int mMaxPos;
	private int mStartPos;
	private int mEndPos;
	private boolean mStartVisible;
	private boolean mEndVisible;
	private int mLastDisplayedStartPos;
	private int mLastDisplayedEndPos;
	private int mOffset;
	private int mOffsetGoal;
	private int mFlingVelocity;
	private int mPlayStartMsec;
	private int mPlayStartOffset;
	private int mPlayEndMsec;
	private Handler mHandler;
	private boolean mIsPlaying;
	private MediaPlayer mPlayer;
	private boolean mCanSeekAccurately;
	private boolean mTouchDragging;
	private float mTouchStart;
	private int mTouchInitialOffset;
	private int mTouchInitialStartPos;
	private int mTouchInitialEndPos;
	private long mWaveformTouchStartMsec;
	private float mDensity;
	private int mMarkerLeftInset;
	private int mMarkerRightInset;
	private int mMarkerTopOffset;
	private int mMarkerBottomOffset;
	private Map<String, Map<String, String>> mLoops;
	// Menu commands
	private static final int CMD_SAVE = 1;
	private static final int CMD_RESET = 2;
	private static final int CMD_TAB = 3;
	private static final int CMD_LYRIC= 4;

	//private static final int CMD_ABOUT = 3;

	// Result codes
	private static final int REQUEST_CODE_RECORD = 1;
	private static final int REQUEST_CODE_CHOOSE_CONTACT = 2;

	/**
	 * This is a special intent action that means "edit a sound file".
	 */
	public static final String EDIT =
			"fr.unice.guitartools.action.EDIT";

	/**
	 * Preference names
	 */
	public static final String PREF_SUCCESS_COUNT = "success_count";

	public static final String PREF_STATS_SERVER_CHECK =
			"stats_server_check";
	public static final String PREF_STATS_SERVER_ALLOWED =
			"stats_server_allowed";

	public static final String PREF_ERROR_COUNT = "error_count";

	public static final String PREF_ERR_SERVER_CHECK =
			"err_server_check";
	public static final String PREF_ERR_SERVER_ALLOWED =
			"err_server_allowed";

	public static final String PREF_UNIQUE_ID = "unique_id";

	/**
	 * Possible codes for PREF_*_SERVER_ALLOWED
	 */
	public static final int SERVER_ALLOWED_UNKNOWN = 0;
	public static final int SERVER_ALLOWED_NO = 1;
	public static final int SERVER_ALLOWED_YES = 2;

	/**
	 * Server url
	 */
	public static final String STATS_SERVER_URL =
			"http://ringdroid.appspot.com/add";
	public static final String ERR_SERVER_URL =
			"http://ringdroid.appspot.com/err";

	//
	// Public methods and protected overrides
	//

	/** Called with the activity is first created. */
	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);

		mRecordingFilename = null;
		mRecordingUri = null;
		mPlayer = null;
		mIsPlaying = false;
		mLoops = new HashMap<String, Map<String,String>>();

		Intent intent = getIntent();

		if (intent.getBooleanExtra("privacy", false)) {
			showServerPrompt(true);
			return;
		}

		// If the GuitarTools media select activity was launched via a
		// GET_CONTENT intent, then we shouldn't display a "d"
		// message when the user saves, we should just return whatever
		// they create.
		mWasGetContentIntent = intent.getBooleanExtra(
				"was_get_content_intent", false);

		mFilename = intent.getData().toString();



		mSoundFile = null;
		mKeyDown = false;

		/*
		 * to record
		 */
		if (mFilename.equals("record")) {
			try {
				Intent recordIntent = new Intent(
						MediaStore.Audio.Media.RECORD_SOUND_ACTION);
				startActivityForResult(recordIntent, REQUEST_CODE_RECORD);
			} catch (Exception e) {
				showFinalAlert(e, R.string.record_error);
			}
		}

		mHandler = new Handler();

		mLoopFileName = mFilename + LoopExtension;
		mSelectAllSong = false;
		LoadLoops(new File(mLoopFileName));
		
		loadGui();

		mHandler.postDelayed(mTimerRunnable, 100);

		if (!mFilename.equals("record")) {
			loadFromFile();
		}
		
	}

	public int getmStartPos() {
		return mStartPos;
	}

	public void setmStartPos(int mStartPos) {
		this.mStartPos = mStartPos;
	}

	public int getmEndPos() {
		return mEndPos;
	}

	public void setmEndPos(int mEndPos) {
		this.mEndPos = mEndPos;
	}

	/** Called with the activity is finally destroyed. */
	@Override
	protected void onDestroy() {
		Log.i("GuitarTools", "EditActivity OnDestroy");

		if (mPlayer != null && mPlayer.isPlaying()) {
			mPlayer.stop();
		}
		mPlayer = null;
		mLoops.clear();
		mLoops = null;
		if (mRecordingFilename != null) {
			try {
				if (!new File(mRecordingFilename).delete()) {
					showFinalAlert(new Exception(), R.string.delete_tmp_error);
				}

				getContentResolver().delete(mRecordingUri, null, null);
			} catch (SecurityException e) {
				showFinalAlert(e, R.string.delete_tmp_error);
			}
		}

		super.onDestroy();
	}

	/** Called with an Activity we started with an Intent returns. */
	@Override
	protected void onActivityResult(int requestCode,
			int resultCode,
			Intent dataIntent) {
		if (requestCode == REQUEST_CODE_CHOOSE_CONTACT) {
			// The user finished saving their ringtone and they're
			// just applying it to a contact.  When they return here,
			// they're done.
			sendStatsToServerIfAllowedAndFinish();
			return;
		}

		if (requestCode != REQUEST_CODE_RECORD) {
			return;
		}

		if (resultCode != RESULT_OK) {
			finish();
			return;
		}

		if (dataIntent == null) {
			finish();
			return;
		}

		// Get the recorded file and open it, but save the uri and
		// filename so that we can delete them when we exit; the
		// recorded file is only temporary and only the edited & saved
		// ringtone / other sound will stick around.
		mRecordingUri = dataIntent.getData();
		mRecordingFilename = getFilenameFromUri(mRecordingUri);
		mFilename = mRecordingFilename;
		loadFromFile();
	}

	/**
	 * Called when the orientation changes and/or the keyboard is shown
	 * or hidden.  We don't need to recreate the whole activity in this
	 * case, but we do need to redo our layout somewhat.
	 */
	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		final int saveZoomLevel = mWaveformView.getZoomLevel();
		super.onConfigurationChanged(newConfig);

		loadGui();
		enableZoomButtons();

		mHandler.postDelayed(new Runnable() {
			public void run() {
				mStartMarker.requestFocus();
				markerFocus(mStartMarker);

				mWaveformView.setZoomLevel(saveZoomLevel);
				mWaveformView.recomputeHeights(mDensity);

				updateDisplay();
			}
		}, 500);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		MenuItem item;


		item = menu.add(0, CMD_TAB, 0, R.string.menu_tab);
		item.setIcon(R.drawable.menu_tab);

		item = menu.add(0, CMD_LYRIC, 0, R.string.menu_lyric);
		item.setIcon(R.drawable.menu_lyric);


		/*item = menu.add(0, CMD_ABOUT, 0, R.string.menu_about);
        item.setIcon(R.drawable.menu_about);*/

		return true;
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		super.onPrepareOptionsMenu(menu);
		menu.findItem(CMD_TAB).setVisible(true);
		menu.findItem(CMD_LYRIC).setVisible(true);
		//  menu.findItem(CMD_ABOUT).setVisible(true);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		/*case CMD_SAVE:
			onSave();
			return true;*/
//		case CMD_RESET:
//			reset();
//			mOffsetGoal = 0;
//			updateDisplay();
//			return true;
		case CMD_TAB:
			getTab();
			return true;
		case CMD_LYRIC:
			getLyric();
			return true;
			//  case CMD_ABOUT:
			//   onAbout(this);
			// return true;
		default:
			return false;
		}
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_SPACE) {
			onPlay(mStartPos);
			return true;
		}

		return super.onKeyDown(keyCode, event);
	}

	//
	// WaveformListener
	//

	/**
	 * Every time we get a message that our waveform drew, see if we need to
	 * animate and trigger another redraw.
	 */
	public void waveformDraw() {
		mWidth = mWaveformView.getMeasuredWidth();
		if (mOffsetGoal != mOffset && !mKeyDown)
			updateDisplay();
		else if (mIsPlaying) {
			updateDisplay();
		} else if (mFlingVelocity != 0) {
			updateDisplay();
		}
	}

	public void waveformTouchStart(float x) {
		mTouchDragging = true;
		mTouchStart = x;
		mTouchInitialOffset = mOffset;
		mFlingVelocity = 0;
		mWaveformTouchStartMsec = System.currentTimeMillis();
	}

	public void waveformTouchMove(float x) {
		mOffset = trap((int)(mTouchInitialOffset + (mTouchStart - x)));
		updateDisplay();
	}

	public void waveformTouchEnd() {
		mTouchDragging = false;
		mOffsetGoal = mOffset;

		long elapsedMsec = System.currentTimeMillis() -
				mWaveformTouchStartMsec;
		if (elapsedMsec < 300) {
			if (mIsPlaying) {
				int seekMsec = mWaveformView.pixelsToMillisecs(
						(int)(mTouchStart + mOffset));
				if (seekMsec >= mPlayStartMsec &&
						seekMsec < mPlayEndMsec) {
					mPlayer.seekTo(seekMsec - mPlayStartOffset);
				} else {
					handlePause();
				}
			} else {
				onPlay((int)(mTouchStart + mOffset));
			}
		}
	}

	public void waveformFling(float vx) {
		mTouchDragging = false;
		mOffsetGoal = mOffset;
		mFlingVelocity = (int)(-vx);
		updateDisplay();
	}

	//
	// MarkerListener
	//

	public void markerDraw() {
	}

	public void markerTouchStart(MarkerView marker, float x) {
		mTouchDragging = true;
		mTouchStart = x;
		mTouchInitialStartPos = mStartPos;
		mTouchInitialEndPos = mEndPos;
	}

	public void markerTouchMove(MarkerView marker, float x) {
		float delta = x - mTouchStart;

		if (marker == mStartMarker) {
			mStartPos = trap((int)(mTouchInitialStartPos + delta));
			// just move marker by marker
			mEndPos = trap((mTouchInitialEndPos));
		} else {
			mEndPos = trap((int)(mTouchInitialEndPos + delta));
			if (mEndPos < mStartPos)
				mEndPos = mStartPos;
		}

		updateDisplay();
	}

	public void markerTouchEnd(MarkerView marker) {
		mTouchDragging = false;
		if (marker == mStartMarker) {
			setOffsetGoalStart();
		} else {
			setOffsetGoalEnd();
		}
	}

	public void markerLeft(MarkerView marker, int velocity) {
		mKeyDown = true;

		if (marker == mStartMarker) {
			int saveStart = mStartPos;
			mStartPos = trap(mStartPos - velocity);
			mEndPos = trap(mEndPos - (saveStart - mStartPos));
			setOffsetGoalStart();
		}

		if (marker == mEndMarker) {
			if (mEndPos == mStartPos) {
				mStartPos = trap(mStartPos - velocity);
				mEndPos = mStartPos;
			} else {
				mEndPos = trap(mEndPos - velocity);
			}

			setOffsetGoalEnd();
		}

		updateDisplay();
	}

	public void markerRight(MarkerView marker, int velocity) {
		mKeyDown = true;

		if (marker == mStartMarker) {
			int saveStart = mStartPos;
			mStartPos += velocity;
			if (mStartPos > mMaxPos)
				mStartPos = mMaxPos;
			mEndPos += (mStartPos - saveStart);
			if (mEndPos > mMaxPos)
				mEndPos = mMaxPos;

			setOffsetGoalStart();
		}

		if (marker == mEndMarker) {
			mEndPos += velocity;
			if (mEndPos > mMaxPos)
				mEndPos = mMaxPos;

			setOffsetGoalEnd();
		}

		updateDisplay();
	}

	public void markerEnter(MarkerView marker) {
	}

	public void markerKeyUp() {
		mKeyDown = false;
		updateDisplay();
	}

	public void markerFocus(MarkerView marker) {
		mKeyDown = false;
		if (marker == mStartMarker) {
			setOffsetGoalStartNoUpdate();
		} else {
			setOffsetGoalEndNoUpdate();
		}

		// Delay updaing the display because if this focus was in
		// response to a touch event, we want to receive the touch
		// event too before updating the display.
		mHandler.postDelayed(new Runnable() {
			public void run() {
				updateDisplay();
			}
		}, 100);
	}

	//
	// Static About dialog method, also called from RingdroidSelectActivity
	//
	/*
    public static void onAbout(final Activity activity) {
        new AlertDialog.Builder(activity)
            .setTitle(R.string.about_title)
            .setMessage(R.string.about_text)
            .setPositiveButton(R.string.alert_ok_button, null)
            .setCancelable(false)
            .show();        
    }*/

	//
	// Internal methods
	//

	/**
	 * Called from both onCreate and onConfigurationChanged
	 * (if the user switched layouts)
	 */
	private void loadGui() {
		// Inflate our UI from its XML layout description.
		setContentView(R.layout.editor);

		StateListDrawable button_player = new StateListDrawable();

		DisplayMetrics metrics = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(metrics);
		mDensity = metrics.density;

		mMarkerLeftInset = (int)(46 * mDensity);
		mMarkerRightInset = (int)(48 * mDensity);
		mMarkerTopOffset = (int)(10 * mDensity);
		mMarkerBottomOffset = (int)(10 * mDensity);

		mStartText = (TextView)findViewById(R.id.starttext);
		mStartText.addTextChangedListener(mTextWatcher);
		mEndText = (TextView)findViewById(R.id.endtext);
		mEndText.addTextChangedListener(mTextWatcher);
		
		((ImageButton)findViewById(R.id.play)).setBackgroundDrawable(button_player);
		((ImageButton)findViewById(R.id.ffwd)).setBackgroundDrawable(button_player);
		((ImageButton)findViewById(R.id.rew)).setBackgroundDrawable(button_player);
		((ImageButton)findViewById(R.id.zoom_in)).setBackgroundDrawable(button_player);
		((ImageButton)findViewById(R.id.zoom_out)).setBackgroundDrawable(button_player);
		((ImageButton)findViewById(R.id.save_2)).setBackgroundDrawable(button_player);
		((ImageButton)findViewById(R.id.delete_loop)).setBackgroundDrawable(button_player);
		((ImageButton)findViewById(R.id.select_all)).setBackgroundDrawable(button_player);


		mPlayButton = (ImageButton)findViewById(R.id.play);
		mPlayButton.setOnClickListener(mPlayListener);
		mRewindButton = (ImageButton)findViewById(R.id.rew);
		mRewindButton.setOnClickListener(mRewindListener);
		mFfwdButton = (ImageButton)findViewById(R.id.ffwd);
		mFfwdButton.setOnClickListener(mFfwdListener);
		mZoomInButton = (ImageButton)findViewById(R.id.zoom_in);
		mZoomInButton.setOnClickListener(mZoomInListener);
		mZoomOutButton = (ImageButton)findViewById(R.id.zoom_out);
		mZoomOutButton.setOnClickListener(mZoomOutListener);
		/*mSaveButton = (ImageButton)findViewById(R.id.save);
		mSaveButton.setOnClickListener(mSaveListener);*/
		mSaveButton_2 = (ImageButton)findViewById(R.id.save_2);
		mSaveButton_2.setOnClickListener(mSaveListener_2);
		mSelectAllButton = (ImageButton)findViewById(R.id.select_all);
		mSelectAllButton.setOnClickListener(mSelectAllListener);
		mDeleteLoop = (ImageButton)findViewById(R.id.delete_loop);
		mDeleteLoop.setOnClickListener(mDeleteLoopListener);

		TextView markStartButton = (TextView) findViewById(R.id.mark_start);
		markStartButton.setOnClickListener(mMarkStartListener);
		TextView markEndButton = (TextView) findViewById(R.id.mark_end);
		markEndButton.setOnClickListener(mMarkStartListener);

		enableDisableButtons();

		mWaveformView = (WaveformView)findViewById(R.id.waveform);
		mWaveformView.setListener(this);

		mInfo = (TextView)findViewById(R.id.info);
		mInfo.setText(mCaption);

		mMaxPos = 0;
		mLastDisplayedStartPos = -1;
		mLastDisplayedEndPos = -1;

		if (mSoundFile != null) {
			mWaveformView.setSoundFile(mSoundFile);
			mWaveformView.recomputeHeights(mDensity);
			mMaxPos = mWaveformView.maxPos();
		}

		mStartMarker = (MarkerView)findViewById(R.id.startmarker);
		mStartMarker.setListener(this);
		mStartMarker.setAlpha(255);
		mStartMarker.setFocusable(true);
		mStartMarker.setFocusableInTouchMode(true);
		mStartVisible = true;

		mEndMarker = (MarkerView)findViewById(R.id.endmarker);
		mEndMarker.setListener(this);
		mEndMarker.setAlpha(255);
		mEndMarker.setFocusable(true);
		mEndMarker.setFocusableInTouchMode(true);
		mEndVisible = true;
		mFfwdButton.setEnabled(true);
		mRewindButton.setEnabled(true);
		updateDisplay();
	}

	private void loadFromFile() {
		mFile = new File(mFilename);
		mExtension = getExtensionFromFilename(mFilename);
		SongMetadataReader metadataReader = new SongMetadataReader(
				this, mFilename);
		mTitle = metadataReader.mTitle;
		mArtist = metadataReader.mArtist;
		mAlbum = metadataReader.mAlbum;
		mYear = metadataReader.mYear;
		mGenre = metadataReader.mGenre;

		String titleLabel = mTitle;
		if (mArtist != null && mArtist.length() > 0) {
			titleLabel += " - " + mArtist;
		}
		setTitle(titleLabel);

		mLoadingStartTime = System.currentTimeMillis();
		mLoadingLastUpdateTime = System.currentTimeMillis();
		mLoadingKeepGoing = true;
		mProgressDialog = new ProgressDialog(RingdroidEditActivity.this);
		mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		mProgressDialog.setTitle(R.string.progress_dialog_loading);
		mProgressDialog.setCancelable(true);
		mProgressDialog.setOnCancelListener(
				new DialogInterface.OnCancelListener() {
					public void onCancel(DialogInterface dialog) {
						mLoadingKeepGoing = false;
					}
				});
		mProgressDialog.show();

		final CheapSoundFile.ProgressListener listener =
				new CheapSoundFile.ProgressListener() {
			public boolean reportProgress(double fractionComplete) {
				long now = System.currentTimeMillis();
				if (now - mLoadingLastUpdateTime > 100) {
					mProgressDialog.setProgress(
							(int)(mProgressDialog.getMax() *
									fractionComplete));
					mLoadingLastUpdateTime = now;
				}
				return mLoadingKeepGoing;
			}
		};

		// Create the MediaPlayer in a background thread
		mCanSeekAccurately = false;
		new Thread() {
			@Override
			public void run() {
				mCanSeekAccurately = SeekTest.CanSeekAccurately(
						getPreferences(Context.MODE_PRIVATE));

				System.out.println("Seek test done, creating media player.");
				try {

					MediaPlayer player = new MediaPlayer();
					player.setDataSource(mFile.getAbsolutePath());
					player.setAudioStreamType(AudioManager.STREAM_MUSIC);
					player.prepare();
					mPlayer = player;
					player.setLooping(true);
				} catch (final java.io.IOException e) {
					Runnable runnable = new Runnable() {
						public void run() {
							handleFatalError(
									"ReadError",
									getResources().getText(R.string.read_error),
									e);
						}
					};
					mHandler.post(runnable);
				};
			}
		}.start();

		// Load the sound file in a background thread
		new Thread() { 
			@Override
			public void run() { 
				try {
					mSoundFile = CheapSoundFile.create(mFile.getAbsolutePath(),
							listener);

					if (mSoundFile == null) {
						mProgressDialog.dismiss();
						String name = mFile.getName().toLowerCase();
						String[] components = name.split("\\.");
						String err;
						if (components.length < 2) {
							err = getResources().getString(
									R.string.no_extension_error);
						} else {
							err = getResources().getString(
									R.string.bad_extension_error) + " " +
									components[components.length - 1];
						}
						final String finalErr = err;
						Runnable runnable = new Runnable() {
							public void run() {
								handleFatalError(
										"UnsupportedExtension",
										finalErr,
										new Exception());
							}
						};
						mHandler.post(runnable);
						return;
					}
				} catch (final Exception e) {
					mProgressDialog.dismiss();
					e.printStackTrace();
					mInfo.setText(e.toString());

					Runnable runnable = new Runnable() {
						public void run() {
							handleFatalError(
									"ReadError",
									getResources().getText(R.string.read_error),
									e);
						}
					};
					mHandler.post(runnable);
					return;
				}
				mProgressDialog.dismiss(); 
				if (mLoadingKeepGoing) {
					Runnable runnable = new Runnable() {
						public void run() {
							finishOpeningSoundFile();
						}
					};
					mHandler.post(runnable);
				} else {
					RingdroidEditActivity.this.finish();
				}
			} 
		}.start();
	}

	private void finishOpeningSoundFile() {
		mWaveformView.setSoundFile(mSoundFile);
		mWaveformView.recomputeHeights(mDensity);

		mMaxPos = mWaveformView.maxPos();
		mLastDisplayedStartPos = -1;
		mLastDisplayedEndPos = -1;

		mTouchDragging = false;

		mOffset = 0;
		mOffsetGoal = 0;
		mFlingVelocity = 0;
		reset();
		if (mEndPos > mMaxPos)
			mEndPos = mMaxPos;

		// detail of song
		mCaption = 
				mSoundFile.getFiletype() + ", " +
						mSoundFile.getSampleRate() + " Hz, " +
						mSoundFile.getAvgBitrateKbps() + " kbps, " +
						formatTime(mMaxPos) + " " +
						getResources().getString(R.string.time_seconds);
		mInfo.setText(mCaption);

		updateDisplay();
	}

	private synchronized void updateDisplay() {
		if (mIsPlaying) {
			int now = mPlayer.getCurrentPosition() + mPlayStartOffset;
			int frames = mWaveformView.millisecsToPixels(now);
			mWaveformView.setPlayback(frames);
			setOffsetGoalNoUpdate(frames - mWidth / 2);
			if (now >= mPlayEndMsec) {
				handlePause();
			}
		}

		if (!mTouchDragging) {
			int offsetDelta;

			if (mFlingVelocity != 0) {
				float saveVel = mFlingVelocity;

				offsetDelta = mFlingVelocity / 30;
				if (mFlingVelocity > 80) {
					mFlingVelocity -= 80;
				} else if (mFlingVelocity < -80) {
					mFlingVelocity += 80;
				} else {
					mFlingVelocity = 0;
				}

				mOffset += offsetDelta;

				if (mOffset + mWidth / 2 > mMaxPos) {
					mOffset = mMaxPos - mWidth / 2;
					mFlingVelocity = 0;
				}
				if (mOffset < 0) {
					mOffset = 0;
					mFlingVelocity = 0;
				}
				mOffsetGoal = mOffset;
			} else {
				offsetDelta = mOffsetGoal - mOffset;

				if (offsetDelta > 10)
					offsetDelta = offsetDelta / 10;
				else if (offsetDelta > 0)
					offsetDelta = 1;
				else if (offsetDelta < -10)
					offsetDelta = offsetDelta / 10;
				else if (offsetDelta < 0)
					offsetDelta = -1;
				else
					offsetDelta = 0;

				mOffset += offsetDelta;
			}
		}

		mWaveformView.setParameters(mStartPos, mEndPos, mOffset);
		mWaveformView.invalidate();

		mStartMarker.setContentDescription(
				getResources().getText(R.string.start_marker) + " " +
						formatTime(mStartPos));
		mEndMarker.setContentDescription(
				getResources().getText(R.string.end_marker) + " " +
						formatTime(mEndPos));

		int startX = mStartPos - mOffset - mMarkerLeftInset;
		if (startX + mStartMarker.getWidth() >= 0) {
			if (!mStartVisible) {
				// Delay this to avoid flicker
				mHandler.postDelayed(new Runnable() {
					public void run() {
						mStartVisible = true;
						mStartMarker.setAlpha(255);
					}
				}, 0);
			}
		} else {
			if (mStartVisible) {
				mStartMarker.setAlpha(0);
				mStartVisible = false;
			}
			startX = 0;
		}

		int endX = mEndPos - mOffset - mEndMarker.getWidth() +
				mMarkerRightInset;
		if (endX + mEndMarker.getWidth() >= 0) {
			if (!mEndVisible) {
				// Delay this to avoid flicker
				mHandler.postDelayed(new Runnable() {
					public void run() {
						mEndVisible = true;
						mEndMarker.setAlpha(255);
					}
				}, 0);
			}
		} else {
			if (mEndVisible) {
				mEndMarker.setAlpha(0);
				mEndVisible = false;
			}
			endX = 0;
		}

		mStartMarker.setLayoutParams(
				new AbsoluteLayout.LayoutParams(
						LayoutParams.WRAP_CONTENT,
						LayoutParams.WRAP_CONTENT,
						startX,
						mMarkerTopOffset));

		mEndMarker.setLayoutParams(
				new AbsoluteLayout.LayoutParams(
						LayoutParams.WRAP_CONTENT,
						LayoutParams.WRAP_CONTENT,
						endX,
						mWaveformView.getMeasuredHeight() -
						mEndMarker.getHeight() - mMarkerBottomOffset));
	}

	/**
	 * retrieve tab in function of song which is playing
	 */
	private void getTab(){
		String url = "http://www.911tabs.com/search.php?search=" + getmTitle().replace(" ", "+") + "&type=song";  
		Intent i = new Intent(Intent.ACTION_VIEW);  
		i.setData(Uri.parse(url));  
		startActivity(i); 
	}

	/**
	 * retrieve lyric in function of song which is playing
	 */
	private void getLyric(){
		String url = "http://www.azlyrics.com/lyrics/" + getmArtist().toLowerCase().replace(" ", "") + "/" + getmTitle().toLowerCase().replace(" ", "") + ".html";  
		Intent i = new Intent(Intent.ACTION_VIEW);  
		i.setData(Uri.parse(url));  
		startActivity(i); 
	}

	private Runnable mTimerRunnable = new Runnable() {
		public void run() {
			// Updating an EditText is slow on Android.  Make sure
			// we only do the update if the text has actually changed.
			if (mStartPos != mLastDisplayedStartPos &&
					!mStartText.hasFocus()) {
				mStartText.setText(formatTime(mStartPos));
				mLastDisplayedStartPos = mStartPos;
			}
			if (mEndPos != mLastDisplayedEndPos &&
					!mEndText.hasFocus()) {
				mEndText.setText(formatTime(mEndPos));
				mLastDisplayedEndPos = mEndPos;
			}
			mHandler.postDelayed(mTimerRunnable, 100);
		}
	};

	private void enableDisableButtons() {
		if (mIsPlaying) {
			mPlayButton.setImageResource(R.drawable.pause_loop);
			mPlayButton.setContentDescription(getResources().getText(R.string.stop));
		} else {
			mPlayButton.setImageResource(R.drawable.play_loop);
			mPlayButton.setContentDescription(getResources().getText(R.string.play));
		}
	}

	private void resetPositions() {
		File fileIn = new File(mLoopFileName);
		if (fileIn.exists() == true) {
			for (Map<String, String> l_Map : mLoops.values()){
				setmTitle(l_Map.get("Title"));
				mStartPos = Integer.parseInt(l_Map.get("StartLoop"));
				mEndPos = Integer.parseInt(l_Map.get("EndLoop"));
				break;
			}
		}
		else {
			reset();
		}
	}

	private void reset() {
		mStartPos = mWaveformView.secondsToPixels(0.0);
		mEndPos = mWaveformView.secondsToPixels(15.0);

	}

	private int trap(int pos) {
		if (pos < 0)
			return 0;
		if (pos > mMaxPos)
			return mMaxPos;
		return pos;
	}

	private void setOffsetGoalStart() {
		setOffsetGoal(mStartPos - mWidth / 2);
	}

	private void setOffsetGoalStartNoUpdate() {
		setOffsetGoalNoUpdate(mStartPos - mWidth / 2);
	}

	private void setOffsetGoalEnd() {
		setOffsetGoal(mEndPos - mWidth / 2);
	}

	private void setOffsetGoalEndNoUpdate() {
		setOffsetGoalNoUpdate(mEndPos - mWidth / 2);
	}

	private void setOffsetGoal(int offset) {
		setOffsetGoalNoUpdate(offset);
		updateDisplay();
	}

	private void setOffsetGoalNoUpdate(int offset) {
		if (mTouchDragging) {
			return;
		}

		mOffsetGoal = offset;
		if (mOffsetGoal + mWidth / 2 > mMaxPos)
			mOffsetGoal = mMaxPos - mWidth / 2;
		if (mOffsetGoal < 0)
			mOffsetGoal = 0;
	}

	private String formatTime(int pixels) {
		if (mWaveformView != null && mWaveformView.isInitialized()) {
			return formatDecimal(mWaveformView.pixelsToSeconds(pixels));
		} else {
			return "";
		}
	}

	private String formatDecimal(double x) {
		int xWhole = (int)x;
		int xFrac = (int)(100 * (x - xWhole) + 0.5);

		if (xFrac >= 100) {
			xWhole++; //Round up
			xFrac -= 100; //Now we need the remainder after the round up
			if (xFrac < 10) {
				xFrac *= 10; //we need a fraction that is 2 digits long
			}
		}

		if (xFrac < 10)
			return xWhole + ".0" + xFrac;
		else
			return xWhole + "." + xFrac;
	}

	private synchronized void handlePause() {
		if (mPlayer != null && mPlayer.isPlaying()) {
			mPlayer.pause();
		}
		mWaveformView.setPlayback(-1);
		mIsPlaying = false;
		enableDisableButtons();
	}

	private synchronized void onPlay(int startPosition) {
		if (mIsPlaying) {
			handlePause();
			mFfwdButton.setEnabled(true);
			mRewindButton.setEnabled(true);
			return;
		}

		if (mPlayer == null) {
			// Not initialized yet
			return;
		}

		try {
			mPlayStartMsec = mWaveformView.pixelsToMillisecs(startPosition);
			if (startPosition < mStartPos) {
				mPlayEndMsec = mWaveformView.pixelsToMillisecs(mStartPos);
			} else if (startPosition > mEndPos) {
				mPlayEndMsec = mWaveformView.pixelsToMillisecs(mMaxPos);
			} else {
				mPlayEndMsec = mWaveformView.pixelsToMillisecs(mEndPos);
			}

			mPlayStartOffset = 0;

			int startFrame = mWaveformView.secondsToFrames(
					mPlayStartMsec * 0.001);
			int endFrame = mWaveformView.secondsToFrames(
					mPlayEndMsec * 0.001);
			int startByte = mSoundFile.getSeekableFrameOffset(startFrame);
			int endByte = mSoundFile.getSeekableFrameOffset(endFrame);
			if (mCanSeekAccurately && startByte >= 0 && endByte >= 0) {
				try {
					mPlayer.reset();
					mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
					FileInputStream subsetInputStream = new FileInputStream(
							mFile.getAbsolutePath());
					mPlayer.setDataSource(subsetInputStream.getFD(),
							startByte, endByte - startByte);
					mPlayer.prepare();
					mPlayStartOffset = mPlayStartMsec;

				} catch (Exception e) {
					System.out.println("Exception trying to play file subset");
					mPlayer.reset();
					mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
					mPlayer.setDataSource(mFile.getAbsolutePath());
					mPlayer.prepare();
					mPlayStartOffset = 0;
				}
			}

			mPlayer.setOnCompletionListener(new OnCompletionListener() {
				public synchronized void onCompletion(MediaPlayer arg0) {
					handlePause();
				}
			});
			mIsPlaying = true;
			mFfwdButton.setEnabled(false);
			mRewindButton.setEnabled(false);
			if (mPlayStartOffset == 0) {
				mPlayer.seekTo(mPlayStartMsec);
			}

			mPlayer.start();
			updateDisplay();
			enableDisableButtons();
			//  mPlayer.setLooping(true);

		} catch (Exception e) {
			showFinalAlert(e, R.string.play_error);
			return;
		}
	}

	/**
	 * Show a "final" alert dialog that will exit the activity
	 * after the user clicks on the OK button.  If an exception
	 * is passed, it's assumed to be an error condition, and the
	 * dialog is presented as an error, and the stack trace is
	 * logged.  If there's no exception, it's a success message.
	 */
	private void showFinalAlert(Exception e, CharSequence message) {
		CharSequence title;
		if (e != null) {
			Log.e("GuitarTools", "Error: " + message);
			Log.e("GuitarTools", getStackTrace(e));
			title = getResources().getText(R.string.alert_title_failure);
			setResult(RESULT_CANCELED, new Intent());
		} else {
			Log.i("GuitarTools", "Success: " + message);
			title = getResources().getText(R.string.alert_title_success);
		}

		new AlertDialog.Builder(RingdroidEditActivity.this)
		.setTitle(title)
		.setMessage(message)
		.setPositiveButton(
				R.string.alert_ok_button,
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog,
							int whichButton) {
						finish();
					}
				})
				.setCancelable(false)
				.show();
	}

	private void showFinalAlert(Exception e, int messageResourceId) {
		showFinalAlert(e, getResources().getText(messageResourceId));
	}

	private String makeRingtoneFilename(CharSequence title, String extension) {
		String parentdir;
		switch(mNewFileKind) {
		default:
		case FileSaveDialog.FILE_KIND_MUSIC:
			parentdir = "/sdcard/media/audio/music";
			break;
		case FileSaveDialog.FILE_KIND_ALARM:
			parentdir = "/sdcard/media/audio/alarms";
			break;
		case FileSaveDialog.FILE_KIND_NOTIFICATION:
			parentdir = "/sdcard/media/audio/notifications";
			break;
		case FileSaveDialog.FILE_KIND_RINGTONE:
			parentdir = "/sdcard/media/audio/ringtones";
			break;
		}

		// Create the parent directory
		File parentDirFile = new File(parentdir);
		parentDirFile.mkdirs();

		// If we can't write to that special path, try just writing
		// directly to the sdcard
		if (!parentDirFile.isDirectory()) {
			parentdir = "/sdcard";
		}

		// Turn the title into a filename
		String filename = "";
		for (int i = 0; i < title.length(); i++) {
			if (Character.isLetterOrDigit(title.charAt(i))) {
				filename += title.charAt(i);
			}
		}

		// Try to make the filename unique
		String path = null;
		for (int i = 0; i < 100; i++) {
			String testPath;
			if (i > 0)
				testPath = parentdir + "/" + filename + i + extension;
			else
				testPath = parentdir + "/" + filename + extension;

			try {
				RandomAccessFile f = new RandomAccessFile(
						new File(testPath), "r");
			} catch (Exception e) {
				// Good, the file didn't exist
				path = testPath;
				break;
			}
		}

		return path;
	}

	private void saveRingtone(final CharSequence title) {
		final String outPath = makeRingtoneFilename(title, mExtension);

		if (outPath == null) {
			showFinalAlert(new Exception(), R.string.no_unique_filename);
			return;
		}

		mDstFilename = outPath;

		double startTime = mWaveformView.pixelsToSeconds(mStartPos);
		double endTime = mWaveformView.pixelsToSeconds(mEndPos);
		final int startFrame = mWaveformView.secondsToFrames(startTime);
		final int endFrame = mWaveformView.secondsToFrames(endTime);
		final int duration = (int)(endTime - startTime + 0.5);

		// Create an indeterminate progress dialog
		mProgressDialog = new ProgressDialog(this);
		mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
		mProgressDialog.setTitle(R.string.progress_dialog_saving);
		mProgressDialog.setIndeterminate(true);
		mProgressDialog.setCancelable(false);
		mProgressDialog.show();

		// Save the sound file in a background thread
		new Thread() { 
			@Override
			public void run() { 
				final File outFile = new File(outPath);
				try {
					// Write the new file
					mSoundFile.WriteFile(outFile,
							startFrame,
							endFrame - startFrame);

					// Try to load the new file to make sure it worked
					final CheapSoundFile.ProgressListener listener =
							new CheapSoundFile.ProgressListener() {
						public boolean reportProgress(double frac) {
							// Do nothing - we're not going to try to
							// estimate when reloading a saved sound
							// since it's usually fast, but hard to
							// estimate anyway.
							return true;  // Keep going
						}
					};
					CheapSoundFile.create(outPath, listener);
				} catch (Exception e) {
					mProgressDialog.dismiss();

					CharSequence errorMessage;
					if (e.getMessage().equals("No space left on device")) {
						errorMessage = getResources().getText(
								R.string.no_space_error);
						e = null;
					} else {
						errorMessage = getResources().getText(
								R.string.write_error);
					}

					final CharSequence finalErrorMessage = errorMessage;
					final Exception finalException = e;
					Runnable runnable = new Runnable() {
						public void run() {
							handleFatalError(
									"WriteError",
									finalErrorMessage,
									finalException);
						}
					};
					mHandler.post(runnable);
					return;
				}

				mProgressDialog.dismiss();

				Runnable runnable = new Runnable() {
					public void run() {
						afterSavingRingtone(title,
								outPath,
								outFile,
								duration);
					}
				};
				mHandler.post(runnable);
			}
		}.start();
	}

	private void afterSavingRingtone(CharSequence title,
			String outPath,
			File outFile,
			int duration) {
		long length = outFile.length();
		if (length <= 512) {
			outFile.delete();
			new AlertDialog.Builder(this)
			.setTitle(R.string.alert_title_failure)
			.setMessage(R.string.too_small_error)
			.setPositiveButton(R.string.alert_ok_button, null)
			.setCancelable(false)
			.show();
			return;
		}

		// Create the database record, pointing to the existing file path

		long fileSize = outFile.length();
		String mimeType = "audio/mpeg";

		String artist = "" + getResources().getText(R.string.artist_name);

		ContentValues values = new ContentValues();
		values.put(MediaStore.MediaColumns.DATA, outPath);
		values.put(MediaStore.MediaColumns.TITLE, title.toString());
		values.put(MediaStore.MediaColumns.SIZE, fileSize);
		values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);

		values.put(AudioColumns.ARTIST, artist);
		values.put(AudioColumns.DURATION, duration);

		values.put(AudioColumns.IS_RINGTONE,
				mNewFileKind == FileSaveDialog.FILE_KIND_RINGTONE);
		values.put(AudioColumns.IS_NOTIFICATION,
				mNewFileKind == FileSaveDialog.FILE_KIND_NOTIFICATION);
		values.put(AudioColumns.IS_ALARM,
				mNewFileKind == FileSaveDialog.FILE_KIND_ALARM);
		values.put(AudioColumns.IS_MUSIC,
				mNewFileKind == FileSaveDialog.FILE_KIND_MUSIC);

		// Insert it into the database
		Uri uri = MediaStore.Audio.Media.getContentUriForPath(outPath);
		final Uri newUri = getContentResolver().insert(uri, values);
		setResult(RESULT_OK, new Intent().setData(newUri));

		// Update a preference that counts how many times we've
		// successfully saved a ringtone or other audio
		SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
		int successCount = prefs.getInt(PREF_SUCCESS_COUNT, 0);
		SharedPreferences.Editor prefsEditor = prefs.edit();
		prefsEditor.putInt(PREF_SUCCESS_COUNT, successCount + 1);
		prefsEditor.commit();

		// If Ringdroid was launched to get content, just return
		if (mWasGetContentIntent) {
			sendStatsToServerIfAllowedAndFinish();
			return;
		}

		// There's nothing more to do with music or an alarm.  Show a
		// success message and then quit.
		if (mNewFileKind == FileSaveDialog.FILE_KIND_MUSIC ||
				mNewFileKind == FileSaveDialog.FILE_KIND_ALARM) {
			Toast.makeText(this,
					R.string.save_success_message,
					Toast.LENGTH_SHORT)
					.show();
			sendStatsToServerIfAllowedAndFinish();
			return;
		}

		// If it's a notification, give the user the option of making
		// this their default notification.  If they say no, we're finished.
		if (mNewFileKind == FileSaveDialog.FILE_KIND_NOTIFICATION) {
			new AlertDialog.Builder(RingdroidEditActivity.this)
			.setTitle(R.string.alert_title_success)
			.setMessage(R.string.set_default_notification)
			.setPositiveButton(R.string.alert_yes_button,
					new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog,
						int whichButton) {
					RingtoneManager.setActualDefaultRingtoneUri(
							RingdroidEditActivity.this,
							RingtoneManager.TYPE_NOTIFICATION,
							newUri);
					sendStatsToServerIfAllowedAndFinish();
				}
			})
			.setNegativeButton(
					R.string.alert_no_button,
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog,
								int whichButton) {
							sendStatsToServerIfAllowedAndFinish();
						}
					})
					.setCancelable(false)
					.show();
			return;
		}

		// If we get here, that means the type is a ringtone.  There are
		// three choices: make this your default ringtone, assign it to a
		// contact, or do nothing.

		final Handler handler = new Handler() {
			@Override
			public void handleMessage(Message response) {
				int actionId = response.arg1;
				switch (actionId) {
				case R.id.button_make_default:
					RingtoneManager.setActualDefaultRingtoneUri(
							RingdroidEditActivity.this,
							RingtoneManager.TYPE_RINGTONE,
							newUri);
					Toast.makeText(
							RingdroidEditActivity.this,
							R.string.default_ringtone_success_message,
							Toast.LENGTH_SHORT)
							.show();
					sendStatsToServerIfAllowedAndFinish();
					break;
				case R.id.button_choose_contact:
					chooseContactForRingtone(newUri);
					break;
				default:
				case R.id.button_do_nothing:
					sendStatsToServerIfAllowedAndFinish();
					break;
				}
			}
		};
		Message message = Message.obtain(handler);
		// AfterSaveActionDialog_old dlog = new AfterSaveActionDialog_old(
		//   this, message);
		// dlog.show();
	}

	private void chooseContactForRingtone(Uri uri) {
		try {
			Intent intent = new Intent(Intent.ACTION_EDIT, uri);
			intent.setClassName(
					"com.ringdroid",
					"com.ringdroid.ChooseContactActivity");
			startActivityForResult(intent, REQUEST_CODE_CHOOSE_CONTACT);
		} catch (Exception e) {
			Log.e("Ringdroid", "Couldn't open Choose Contact window");
		}
	}

	private void handleFatalError(
			final CharSequence errorInternalName,
			final CharSequence errorString,
			final Exception exception) {
		Log.i("Ringdroid", "handleFatalError");

		SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
		int failureCount = prefs.getInt(PREF_ERROR_COUNT, 0);
		final SharedPreferences.Editor prefsEditor = prefs.edit();
		prefsEditor.putInt(PREF_ERROR_COUNT, failureCount + 1);
		prefsEditor.commit();

		// Check if we already have a pref for whether or not we can
		// contact the server.
		int serverAllowed = prefs.getInt(PREF_ERR_SERVER_ALLOWED,
				SERVER_ALLOWED_UNKNOWN);

		if (serverAllowed == SERVER_ALLOWED_NO) {
			Log.i("Ringdroid", "ERR: SERVER_ALLOWED_NO");

			// Just show a simple "write error" message
			showFinalAlert(exception, errorString);
			return;
		}

		if (serverAllowed == SERVER_ALLOWED_YES) {
			Log.i("Ringdroid", "SERVER_ALLOWED_YES");

			new AlertDialog.Builder(RingdroidEditActivity.this)
			.setTitle(R.string.alert_title_failure)
			.setMessage(errorString)
			.setPositiveButton(
					R.string.alert_ok_button,
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog,
								int whichButton) {
							sendErrToServerAndFinish(errorInternalName,
									exception);
							return;
						}
					})
					.setCancelable(false)
					.show();
			return;
		}

		// The number of times the user must have had a failure before
		// we'll ask them.  Defaults to 1, and each time they click "Later"
		// we double and add 1.
		final int allowServerCheckIndex =
				prefs.getInt(PREF_ERR_SERVER_CHECK, 1);
		if (failureCount < allowServerCheckIndex) {
			Log.i("Ringdroid", "failureCount " + failureCount +
					" is less than " + allowServerCheckIndex);
			// Just show a simple "write error" message
			showFinalAlert(exception, errorString);
			return;
		}

		final SpannableString message = new SpannableString(
				errorString + ". " +
						getResources().getText(R.string.error_server_prompt));
		Linkify.addLinks(message, Linkify.ALL);

		AlertDialog dialog = new AlertDialog.Builder(this)
		.setTitle(R.string.alert_title_failure)
		.setMessage(message)
		.setPositiveButton(
				R.string.server_yes,
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog,
							int whichButton) {
						prefsEditor.putInt(PREF_ERR_SERVER_ALLOWED,
								SERVER_ALLOWED_YES);
						prefsEditor.commit();
						sendErrToServerAndFinish(errorInternalName,
								exception);
					}
				})
				.setNeutralButton(
						R.string.server_later,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog,
									int whichButton) {
								prefsEditor.putInt(PREF_ERR_SERVER_CHECK,
										1 + allowServerCheckIndex * 2);
								Log.i("Ringdroid",
										"Won't check again until " +
												(1 + allowServerCheckIndex * 2) +
										" errors.");
								prefsEditor.commit();
								finish();
							}
						})
						.setNegativeButton(
								R.string.server_never,
								new DialogInterface.OnClickListener() {
									public void onClick(DialogInterface dialog,
											int whichButton) {
										prefsEditor.putInt(PREF_ERR_SERVER_ALLOWED,
												SERVER_ALLOWED_NO);
										prefsEditor.commit();
										finish();
									}
								})
								.setCancelable(false)
								.show();

		// Make links clicky
		((TextView)dialog.findViewById(android.R.id.message))
		.setMovementMethod(LinkMovementMethod.getInstance());
	}

	/*
	private void onSave() {
		if (mIsPlaying) {
			handlePause();
		}

		final Handler handler = new Handler() {
			public void handleMessage(Message response) {
				CharSequence newTitle = (CharSequence)response.obj;
				mNewFileKind = response.arg1;
				saveRingtone(newTitle);
			}
		};
		Message message = Message.obtain(handler);
		FileSaveDialog dlog = new FileSaveDialog(
				this, getResources(), mTitle, message);
		dlog.show();
	}*/

	/**
	 * method allows to save a loop in file
	 */
	private void onSave_2(){
	
		File fileSave = new File(mLoopFileName);
			// test if file already exist for this mFilename ...
			if (fileSave.exists() == false) {
				
				// add info in property file
				String title = mTitle + "_" +UUID.randomUUID();
				AddLoop(title, mStartPos, mEndPos);
				Toast.makeText(this,"Congratulations the loop has been saved !", Toast.LENGTH_LONG) .show();
			} else {
					LoadLoops(fileSave);
					boolean alreadyExists = false;
					if (mLoops.values().size() < 10){
					for(Map<String,String> l_Map : mLoops.values()){
						if (mStartPos == Integer.parseInt(l_Map.get("StartLoop")) && mEndPos == Integer.parseInt(l_Map.get("EndLoop"))){
							alreadyExists=true;
							break;
						}
					}
					if (!alreadyExists){
						String title = mTitle + "_" + UUID.randomUUID();
						AddLoop(title, mStartPos, mEndPos);
						Toast.makeText(this,"Congratulations the loop has been saved !", Toast.LENGTH_LONG) .show();
					}else{
						Toast.makeText(this, "This loop is already saved !", Toast.LENGTH_LONG) .show();
					}
					}else{
						Toast.makeText(this, "Too many loops, please delete at least one before saving !", Toast.LENGTH_LONG) .show();
					}
			}
	}

	private void SaveLoops(){
		Properties prop = new Properties();
		File fileSave = new File(mLoopFileName);
		boolean isFistPass = false;
		for(Map<String, String> l_Map : mLoops.values()){
			Set<Entry<String,String>> l_EntrySet = l_Map.entrySet();
			for(Entry<String,String> l_Entry : l_EntrySet){
				prop.setProperty(l_Entry.getKey(), l_Entry.getValue() );
			}
			try {
			FileOutputStream l_outputStream =new FileOutputStream(fileSave, isFistPass);
			prop.store(l_outputStream, "#");
			l_outputStream.close();
			isFistPass = true;
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
				prop.clear();
	}
	
	private void AddLoop(String inTitle, int inStartPos, int inEndPos){
		Map<String,String> l_Map = new HashMap<String, String>();
		l_Map.put("Title", inTitle);
		l_Map.put("StartLoop",  Integer.toString(inStartPos));
		l_Map.put("EndLoop",  Integer.toString(inEndPos));
		mLoops.put(inTitle, l_Map);
		SaveLoops();
	}
	
	private void DeleteLoop(String inTitle){
		Map<String,String> l_Result = mLoops.remove(inTitle);
		if ( l_Result != null){
			SaveLoops();
		}
	}
	
	private void LoadLoops(File inFile){
		mLoops.clear();
		if ( inFile.exists()){
		try {
			FileReader l_File = new FileReader(inFile);
		
		BufferedReader l_Reader = new BufferedReader(l_File);
		
		boolean stopRead = false;
		while(!stopRead){
			String l_Line =	l_Reader.readLine();
			if(l_Line != null){
			Map<String,String> l_Map = new HashMap<String, String>();
			String l_Title = "";
			while (!l_Line.startsWith("#")){
				String[] l_KeyValues = l_Line.split("=");
				if ( l_KeyValues.length == 2){
					if ( l_KeyValues[0].equalsIgnoreCase("Title")){
						l_Title = l_KeyValues[1];
					}
					l_Map.put(l_KeyValues[0], l_KeyValues[1]);
				}
				l_Line =	l_Reader.readLine();
				if(l_Line == null){
					break;
				}
			}
			if ( !l_Title.equals("")){
				mLoops.put(l_Title, l_Map);
			}
			}else{
			stopRead = true;
			}
		}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		}
	}
	private void onSelectAll(){
		if (mSelectAllSong) {
			mSelectAllSong = false;
			resetPositions();
		} else {
			mSelectAllSong = true;
			int durationCurrentSong = mPlayer.getDuration()/1000;
			mStartPos = mWaveformView.secondsToPixels(0.0);
			mEndPos = mWaveformView.secondsToPixels(durationCurrentSong);
		}
		
		updateDisplay();
	}
	
	private void onDeleteLoop() {
		File fileIn = new File(mLoopFileName);
		
		if (fileIn.exists() == true) {	
				if (mLoops.values().size() < 11){
					String loopToRemove = "";
					for(Map<String,String> l_Map : mLoops.values()){
					if ( mStartPos == Integer.parseInt(l_Map.get("StartLoop")) 
					  && mEndPos == Integer.parseInt(l_Map.get("EndLoop"))){
						loopToRemove = l_Map.get("Title");
						break;
					}
				}
				if(!loopToRemove.equals("")){
					DeleteLoop(loopToRemove);
					Toast.makeText(this, "The loop was correctly deleted ", Toast.LENGTH_LONG) .show();
					mStartPos = mWaveformView.secondsToPixels(0.0);
					mEndPos = mWaveformView.secondsToPixels(15.0);
					updateDisplay();
				}
			} 
		}
		else {
			// desable button delete
		}
		
	}

	private void enableZoomButtons() {
		mZoomInButton.setEnabled(mWaveformView.canZoomIn());
		mZoomOutButton.setEnabled(mWaveformView.canZoomOut());
	}

	/*
		private OnClickListener mSaveListener = new OnClickListener() {
			public void onClick(View sender) {
				onSave();
			}
		};*/

	private OnClickListener mSaveListener_2 = new OnClickListener() {
		public void onClick(View sender) {
			onSave_2();
		}
	};

	private OnClickListener mSelectAllListener =  new OnClickListener() {

		public void onClick(View sender) {
			onSelectAll();

		}
	};
	
	private OnClickListener mDeleteLoopListener =  new OnClickListener() {

		public void onClick(View sender) {
			onDeleteLoop();

		}
	};

	private OnClickListener mPlayListener = new OnClickListener() {
		public void onClick(View sender) {
			onPlay(mStartPos);
			mPlayer.setLooping(true);
		}
	};

	private OnClickListener mZoomInListener = new OnClickListener() {
		public void onClick(View sender) {
			mWaveformView.zoomIn();
			mStartPos = mWaveformView.getStart();
			mEndPos = mWaveformView.getEnd();
			mMaxPos = mWaveformView.maxPos();
			mOffset = mWaveformView.getOffset();
			mOffsetGoal = mOffset;
			enableZoomButtons();
			updateDisplay();
		}
	};

	private OnClickListener mZoomOutListener = new OnClickListener() {
		public void onClick(View sender) {
			mWaveformView.zoomOut();
			mStartPos = mWaveformView.getStart();
			mEndPos = mWaveformView.getEnd();
			mMaxPos = mWaveformView.maxPos();
			mOffset = mWaveformView.getOffset();
			mOffsetGoal = mOffset;
			enableZoomButtons();
			updateDisplay();
		}
	};

	private OnClickListener mRewindListener = new OnClickListener() {
		public void onClick(View sender) {
			
			File file = new File(mLoopFileName);

			if (file.exists() == true) {
					Map<String, String> l_ClosestMap = null;
					for(Map<String,String> l_Map : mLoops.values()){
						if (Integer.parseInt(l_Map.get("StartLoop")) == mStartPos && Integer.parseInt(l_Map.get("EndLoop")) < mEndPos) {
							if ( l_ClosestMap == null){
							l_ClosestMap = l_Map;
							}else{
								if (Integer.parseInt(l_Map.get("StartLoop")) != Integer.parseInt(l_ClosestMap.get("StartLoop")) || Integer.parseInt(l_Map.get("EndLoop")) > Integer.parseInt(l_ClosestMap.get("EndLoop"))) {
								l_ClosestMap = l_Map;
								}
							}
						} else if(Integer.parseInt(l_Map.get("StartLoop")) < mStartPos && Integer.parseInt(l_Map.get("EndLoop")) == mEndPos) {
							if ( l_ClosestMap == null){
							l_ClosestMap = l_Map;
							}else{
								if (Integer.parseInt(l_Map.get("StartLoop")) > Integer.parseInt(l_ClosestMap.get("StartLoop")) ||Integer.parseInt(l_Map.get("EndLoop")) != Integer.parseInt(l_ClosestMap.get("EndLoop"))) {
									l_ClosestMap = l_Map;
									}
							}
						} else if (Integer.parseInt(l_Map.get("StartLoop")) < mStartPos  && Integer.parseInt(l_Map.get("EndLoop")) < mEndPos) {
							if ( l_ClosestMap == null){
							l_ClosestMap = l_Map;
							}else{
								if (Integer.parseInt(l_Map.get("StartLoop")) > Integer.parseInt(l_ClosestMap.get("StartLoop")) ||Integer.parseInt(l_Map.get("EndLoop")) > Integer.parseInt(l_ClosestMap.get("EndLoop"))) {
									l_ClosestMap = l_Map;
									}
							}
						}
					}
					if (l_ClosestMap != null){
						mStartPos = Integer.parseInt(l_ClosestMap.get("StartLoop"));
						mEndPos = Integer.parseInt(l_ClosestMap.get("EndLoop"));
						updateDisplay();
					}
			}
			else {
				// desable button delete
			}
		}
	};

	private OnClickListener mFfwdListener = new OnClickListener() {
		public void onClick(View sender) {
			File file = new File(mLoopFileName);

			if (file.exists() == true) {
					Map<String, String> l_ClosestMap = null;
					for(Map<String,String> l_Map : mLoops.values()){
						if(Integer.parseInt(l_Map.get("StartLoop")) > mStartPos && mEndPos == Integer.parseInt(l_Map.get("EndLoop"))) {
							if ( l_ClosestMap == null){
							l_ClosestMap = l_Map;
							}else{
								if (Integer.parseInt(l_Map.get("StartLoop")) < Integer.parseInt(l_ClosestMap.get("StartLoop")) ||Integer.parseInt(l_Map.get("EndLoop")) != Integer.parseInt(l_ClosestMap.get("EndLoop"))) {
									l_ClosestMap = l_Map;
									}
							}
						} else if (mStartPos == Integer.parseInt(l_Map.get("StartLoop")) && Integer.parseInt(l_Map.get("EndLoop")) > mEndPos) {
							if ( l_ClosestMap == null){
							l_ClosestMap = l_Map;
							}else{
								if (Integer.parseInt(l_Map.get("StartLoop")) != Integer.parseInt(l_ClosestMap.get("StartLoop")) || Integer.parseInt(l_Map.get("EndLoop")) < Integer.parseInt(l_ClosestMap.get("EndLoop"))) {
									l_ClosestMap = l_Map;
									}
								
							}
						} else if (Integer.parseInt(l_Map.get("StartLoop")) > mStartPos && Integer.parseInt(l_Map.get("EndLoop")) > mEndPos) {
							if ( l_ClosestMap == null){
							l_ClosestMap = l_Map;
							}else{
								if (Integer.parseInt(l_Map.get("StartLoop")) < Integer.parseInt(l_ClosestMap.get("StartLoop")) ||Integer.parseInt(l_Map.get("EndLoop")) < Integer.parseInt(l_ClosestMap.get("EndLoop"))) {
									l_ClosestMap = l_Map;
									}
							}
						}
					}
					if (l_ClosestMap != null){
						mStartPos = Integer.parseInt(l_ClosestMap.get("StartLoop"));
						mEndPos = Integer.parseInt(l_ClosestMap.get("EndLoop"));
						updateDisplay();
					}
			}
			else {
				// desable button delete
			}
		}
	};

	private OnClickListener mMarkStartListener = new OnClickListener() {
		public void onClick(View sender) {
			if (mIsPlaying) {
				mStartPos = mWaveformView.millisecsToPixels(
						mPlayer.getCurrentPosition() + mPlayStartOffset);
				updateDisplay();
			}
		}
	};

	private OnClickListener mMarkEndListener = new OnClickListener() {
		public void onClick(View sender) {
			if (mIsPlaying) {
				mEndPos = mWaveformView.millisecsToPixels(
						mPlayer.getCurrentPosition() + mPlayStartOffset);
				updateDisplay();
				handlePause();
			}
		}
	};

	private TextWatcher mTextWatcher = new TextWatcher() {
		public void beforeTextChanged(CharSequence s, int start,
				int count, int after) {
		}

		public void onTextChanged(CharSequence s,
				int start, int before, int count) {
		}

		public void afterTextChanged(Editable s) {
			if (mStartText.hasFocus()) {
				try {
					mStartPos = mWaveformView.secondsToPixels(
							Double.parseDouble(
									mStartText.getText().toString()));
					updateDisplay();
				} catch (NumberFormatException e) {
				}
			}
			if (mEndText.hasFocus()) {
				try {
					mEndPos = mWaveformView.secondsToPixels(
							Double.parseDouble(
									mEndText.getText().toString()));
					updateDisplay();
				} catch (NumberFormatException e) {
				}
			}
		}
	};

	private String getStackTrace(Exception e) {
		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		PrintWriter writer = new PrintWriter(stream, true);
		e.printStackTrace(writer);
		return stream.toString();
	}

	/**
	 * Return extension including dot, like ".mp3"
	 */
	 private String getExtensionFromFilename(String filename) {
		 return filename.substring(filename.lastIndexOf('.'),
				 filename.length());
	 }

	 private String getFilenameFromUri(Uri uri) {
		 Cursor c = managedQuery(uri, null, "", null, null);
		 if (c.getCount() == 0) {
			 return null;
		 }
		 c.moveToFirst();
		 int dataIndex = c.getColumnIndexOrThrow(
				 MediaColumns.DATA);

		 return c.getString(dataIndex);
	 }

	 private void sendStatsToServerIfAllowedAndFinish() {
		 Log.i("Ringdroid", "sendStatsToServerIfAllowedAndFinish");

		 final SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);

		 // Check if we already have a pref for whether or not we can
		 // contact the server.
		 int serverAllowed = prefs.getInt(PREF_STATS_SERVER_ALLOWED,
				 SERVER_ALLOWED_UNKNOWN);
		 if (serverAllowed == SERVER_ALLOWED_NO) {
			 Log.i("Ringdroid", "SERVER_ALLOWED_NO");
			 finish();
			 return;
		 }

		 if (serverAllowed == SERVER_ALLOWED_YES) {
			 Log.i("Ringdroid", "SERVER_ALLOWED_YES");
			 sendStatsToServerAndFinish();
			 return;
		 }

		 // Number of times the user has successfully saved a sound.
		 int successCount = prefs.getInt(PREF_SUCCESS_COUNT, 0);

		 // The number of times the user must have successfully saved
		 // a sound before we'll ask them.  Defaults to 2, and doubles
		 // each time they click "Later".
		 final int allowServerCheckIndex =
				 prefs.getInt(PREF_STATS_SERVER_CHECK, 2);
		 if (successCount < allowServerCheckIndex) {
			 Log.i("Ringdroid", "successCount " + successCount +
					 " is less than " + allowServerCheckIndex);
			 finish();
			 return;
		 }

		 showServerPrompt(false);
	 }

	 void showServerPrompt(final boolean userInitiated) {
		 final SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);

		 final SpannableString message = new SpannableString(
				 getResources().getText(R.string.server_prompt));
		 Linkify.addLinks(message, Linkify.ALL);

		 final AlertDialog dialog = new AlertDialog.Builder(RingdroidEditActivity.this)
		 .setTitle(R.string.server_title)
		 .setMessage(message)
		 .setPositiveButton(
				 R.string.server_yes,
				 new DialogInterface.OnClickListener() {
					 public void onClick(DialogInterface dialog,
							 int whichButton) {
						 SharedPreferences.Editor prefsEditor = prefs.edit();
						 prefsEditor.putInt(PREF_STATS_SERVER_ALLOWED,
								 SERVER_ALLOWED_YES);
						 prefsEditor.commit();
						 if (userInitiated) {
							 finish();
						 } else {
							 sendStatsToServerAndFinish();
						 }
					 }
				 })
				 .setNeutralButton(
						 R.string.server_later,
						 new DialogInterface.OnClickListener() {
							 public void onClick(DialogInterface dialog,
									 int whichButton) {
								 int allowServerCheckIndex =
										 prefs.getInt(PREF_STATS_SERVER_CHECK, 2);
								 int successCount = prefs.getInt(PREF_SUCCESS_COUNT, 0);
								 SharedPreferences.Editor prefsEditor = prefs.edit();
								 if (userInitiated) {
									 prefsEditor.putInt(PREF_STATS_SERVER_CHECK,
											 successCount + 2);

								 } else {
									 prefsEditor.putInt(PREF_STATS_SERVER_CHECK,
											 allowServerCheckIndex * 2);
								 }
								 prefsEditor.commit();
								 finish();
							 }
						 })
						 .setNegativeButton(
								 R.string.server_never,
								 new DialogInterface.OnClickListener() {
									 public void onClick(DialogInterface dialog,
											 int whichButton) {
										 SharedPreferences.Editor prefsEditor = prefs.edit();
										 prefsEditor.putInt(PREF_STATS_SERVER_ALLOWED,
												 SERVER_ALLOWED_NO);
										 if (userInitiated) {
											 // If the user initiated, err on the safe side and disable
											 // sending crash reports too. There's no way to turn them
											 // back on now aside from clearing data from this app, but
											 // it doesn't matter, we don't need error reports from every
											 // user ever.
											 prefsEditor.putInt(PREF_ERR_SERVER_ALLOWED,
													 SERVER_ALLOWED_NO);
										 }
										 prefsEditor.commit();
										 finish();
									 }
								 })
								 .setCancelable(false)
								 .show();

		 // Make links clicky
		 ((TextView)dialog.findViewById(android.R.id.message))
		 .setMovementMethod(LinkMovementMethod.getInstance());
	 }

	 void sendStatsToServerAndFinish() {
		 Log.i("Ringdroid", "sendStatsToServerAndFinish");
		 new Thread() {
			 @Override
			public void run() { 
				 sendToServer(STATS_SERVER_URL, null, null);
			 } 
		 }.start();
		 Log.i("Ringdroid", "sendStatsToServerAndFinish calling finish");
		 finish();
	 }

	 void sendErrToServerAndFinish(final CharSequence errType,
			 final Exception exception) {
		 Log.i("Ringdroid", "sendErrToServerAndFinish");
		 new Thread() {
			 @Override
			public void run() { 
				 sendToServer(ERR_SERVER_URL, errType, exception);
			 } 
		 }.start();
		 Log.i("Ringdroid", "sendErrToServerAndFinish calling finish");
		 finish();
	 }

	 /**
	  * Nothing nefarious about this; the purpose is just to
	  * uniquely identify each user so we don't double-count the same
	  * ringtone - without actually identifying the actual user.
	  */
	 long getUniqueId() {
		 SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
		 long uniqueId = prefs.getLong(PREF_UNIQUE_ID, 0);
		 if (uniqueId == 0) {
			 uniqueId = new Random().nextLong();

			 SharedPreferences.Editor prefsEditor = prefs.edit();
			 prefsEditor.putLong(PREF_UNIQUE_ID, uniqueId);
			 prefsEditor.commit();
		 }

		 return uniqueId;
	 }

	 /**
	  * If the exception is not null, will send the stack trace.
	  */
	 void sendToServer(String serverUrl,
			 CharSequence errType,
			 Exception exception) {
		 if (mTitle == null)
			 return;

		 Log.i("Ringdroid", "sendStatsToServer");

		 boolean isSuccess = (exception == null);

		 StringBuilder postMessage = new StringBuilder();
		 String ringdroidVersion = "unknown";
		 try {
			 ringdroidVersion =
					 getPackageManager().getPackageInfo(getPackageName(), -1)
					 .versionName;
		 } catch (android.content.pm.PackageManager.NameNotFoundException e) {
		 }
		 postMessage.append("ringdroid_version=");
		 postMessage.append(URLEncoder.encode(ringdroidVersion));

		 postMessage.append("&android_version=");
		 postMessage.append(URLEncoder.encode(Build.VERSION.RELEASE));

		 postMessage.append("&unique_id=");
		 postMessage.append(getUniqueId());

		 postMessage.append("&accurate_seek=");
		 postMessage.append(mCanSeekAccurately);

		 if (isSuccess) {
			 postMessage.append("&title=");
			 postMessage.append(URLEncoder.encode(mTitle));
			 if (mArtist != null) {
				 postMessage.append("&artist=");
				 postMessage.append(URLEncoder.encode(mArtist));
			 }
			 if (mAlbum != null) {
				 postMessage.append("&album=");
				 postMessage.append(URLEncoder.encode(mAlbum));
			 }
			 if (mGenre != null) {
				 postMessage.append("&genre=");
				 postMessage.append(URLEncoder.encode(mGenre));
			 }
			 postMessage.append("&year=");
			 postMessage.append(mYear);

			 postMessage.append("&filename=");
			 postMessage.append(URLEncoder.encode(mFilename));

			 // The user's real location is not actually sent, this is just
			 // vestigial code from an old experiment.
			 double latitude = 0.0;
			 double longitude = 0.0;
			 postMessage.append("&user_lat=");
			 postMessage.append(URLEncoder.encode("" + latitude));
			 postMessage.append("&user_lon=");
			 postMessage.append(URLEncoder.encode("" + longitude));

			 SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
			 int successCount = prefs.getInt(PREF_SUCCESS_COUNT, 0);
			 postMessage.append("&success_count=");
			 postMessage.append(URLEncoder.encode("" + successCount));

			 postMessage.append("&bitrate=");
			 postMessage.append(URLEncoder.encode(
					 "" + mSoundFile.getAvgBitrateKbps()));

			 postMessage.append("&channels=");
			 postMessage.append(URLEncoder.encode(
					 "" + mSoundFile.getChannels()));

			 String md5;
			 try {
				 md5 = mSoundFile.computeMd5OfFirst10Frames();
			 } catch (Exception e) {
				 md5 = "";
			 }
			 postMessage.append("&md5=");
			 postMessage.append(URLEncoder.encode(md5));

		 } else {
			 // Error case

			 postMessage.append("&err_type=");
			 postMessage.append(errType);
			 postMessage.append("&err_str=");
			 postMessage.append(URLEncoder.encode(getStackTrace(exception)));

			 postMessage.append("&src_filename=");
			 postMessage.append(URLEncoder.encode(mFilename));

			 if (mDstFilename != null) {
				 postMessage.append("&dst_filename=");
				 postMessage.append(URLEncoder.encode(mDstFilename));
			 }
		 }

		 if (mSoundFile != null) {
			 double framesToSecs = 0.0;
			 double sampleRate = mSoundFile.getSampleRate();
			 if (sampleRate > 0.0) {
				 framesToSecs = mSoundFile.getSamplesPerFrame()
						 * 1.0 / sampleRate;
			 }

			 double songLen = framesToSecs * mSoundFile.getNumFrames();
			 postMessage.append("&songlen=");
			 postMessage.append(URLEncoder.encode("" + songLen));

			 postMessage.append("&sound_type=");
			 postMessage.append(URLEncoder.encode(mSoundFile.getFiletype()));

			 double clipStart = mStartPos * framesToSecs;
			 double clipLen = (mEndPos - mStartPos) * framesToSecs;
			 postMessage.append("&clip_start=");
			 postMessage.append(URLEncoder.encode("" + clipStart));
			 postMessage.append("&clip_len=");
			 postMessage.append(URLEncoder.encode("" + clipLen));
		 }

		 String fileKindName = FileSaveDialog.KindToName(mNewFileKind);
		 postMessage.append("&clip_kind=");
		 postMessage.append(URLEncoder.encode(fileKindName));

		 Log.i("Ringdroid", postMessage.toString());

		 try {
			 int TIMEOUT_MILLISEC = 10000;  // = 10 seconds
			 HttpParams httpParams = new BasicHttpParams();
			 HttpConnectionParams.setConnectionTimeout(httpParams,
					 TIMEOUT_MILLISEC);
			 HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_MILLISEC);
			 HttpClient client = new DefaultHttpClient(httpParams);

			 HttpPost request = new HttpPost(serverUrl);
			 request.setEntity(new ByteArrayEntity(
					 postMessage.toString().getBytes("UTF8")));

			 Log.i("Ringdroid", "Executing request");
			 HttpResponse response = client.execute(request);

			 Log.i("Ringdroid", "Response: " + response.toString());

		 } catch (Exception e) {
			 e.printStackTrace();
		 }
	 }

	 public File getmFile() {
		 return mFile;
	 }

	 public void setmFile(File mFile) {
		 this.mFile = mFile;
	 }

	 public String getmFilename() {
		 return mFilename;
	 }

	 public void setmFilename(String mFilename) {
		 this.mFilename = mFilename;
	 }

	 public String getmArtist() {
		 return mArtist;
	 }

	 public void setmArtist(String mArtist) {
		 this.mArtist = mArtist;
	 }

	 public String getmTitle() {
		 return mTitle;
	 }

	 public void setmTitle(String mTitle) {
		 this.mTitle = mTitle;
	 }
}
